/**
 * Copyright (c) 2009 blurredVision
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or 
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT 
 * OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include "StdAfx.h"
#include "StreamingBuffer_OpenAL.h"
#include <vector>
#include "vorbis/codec.h"

namespace openAL {

StreamingBuffer_OpenAL::StreamingBuffer_OpenAL(const std::string &_fileName)
{
	m_sFileName = _fileName;
	m_bFileOpen = false;
	m_pInfo = NULL;
}

StreamingBuffer_OpenAL::~StreamingBuffer_OpenAL(void)
{
	close();
}

bool StreamingBuffer_OpenAL::fillBuffer(ALuint _uiBufferID) {
	if (!m_bFileOpen || !m_pInfo) return false; 
	
	bool bSuccess = false;
	
	const int BYTES_TO_READ = 4096;		// Number of bytes that are converted per iteration
	int iBufferSize = 524288;			// Size of the xoundbuffer that is passed back
	int endian = 0;						// 0 for Little-Endian ( x86)

	ALenum format;						// The sound data format
	ALsizei freq;						// The frequency of the sound data	
	char rawData[BYTES_TO_READ];		// Array, that holds the converted samples 
	std::vector<char> bufferData;		// The whole buffer data
	long bytes = 1;						
    int bitStream;    
	
	// Check the number of channels... always use 16-bit samples
	if (m_pInfo->channels == 1) {
        format = AL_FORMAT_MONO16;
	}else{
		format = AL_FORMAT_STEREO16;
	}    

    // The frequency of the sampling rate
	freq = m_pInfo->rate; 

	while ((bytes > 0) && (iBufferSize > 0)){ // Keep reading until all is read

		// Read up to a buffer's worth of decoded sound data
		bytes = ov_read(&oggFile, rawData, BYTES_TO_READ, endian, 2, 1, &bitStream);
		
		if (bytes < 0){
			close();	
		} else {
			bSuccess = true;

			// Append to end of buffer
			bufferData.insert(bufferData.end(), rawData, rawData + bytes);
			
			// Decrease number of bytes that are left in the buffer
			iBufferSize -= bytes;
		}		
    }   

	alGetError();
	
	// copy the buffer content into an openAL-Buffer
	if (bSuccess && bufferData.size()) {
		alBufferData(_uiBufferID, format, &bufferData[0], (ALsizei)bufferData.size(), freq);
	} else {
		bSuccess = false;
	}

	int err = alGetError();

	if (err != AL_NO_ERROR) {
		//STDCOUT(err);		
		bSuccess = false;
	}

	return bSuccess;
}

void StreamingBuffer_OpenAL::fillBufferWithSilence(ALuint _uiBufferID, int _iSilence) {
	ALenum	format = AL_FORMAT_STEREO16;
	ALsizei freq = 44100;
	int blockSize = 4; ///< Size of a datablock. 2Chanels * 2Byte per Chanel (16Bit)
	void* pData;

	// Calculate size if Bytes needed
	int sizeOfBuffer = (blockSize * 44100 * _iSilence) / 1000;
	// Buffer needs to be a multiple of the blockSize
	sizeOfBuffer -= (sizeOfBuffer % blockSize);

	// Create pseudo wavedata
	pData = malloc(sizeOfBuffer);
	memset(pData, 0, sizeOfBuffer);

	// Set data to buffer
	alBufferData(_uiBufferID, format, pData, sizeOfBuffer, freq);
	
	// Release pseudo data
	free(pData);	
}

bool StreamingBuffer_OpenAL::open() {
	if (m_bFileOpen) {
		return false;
	}
	
	FILE *f = NULL;							// The file pointer to the oggfile								
	
    // Open for binary reading
	if (m_sFileName != "") {
		fopen_s(&f, m_sFileName.c_str(), "rb");
	} else {
		std::stringstream str;
		str << "File name is empty, Alde!";
		coreGetLog()->WriteError(str.str(),__FILE__,__LINE__);
		return false;	
	}
   
	if (f == NULL){
		std::stringstream str;
		str << "Cannot open '" << m_sFileName << "' for reading..." << std::endl;
		coreGetLog()->WriteError(str.str(),__FILE__,__LINE__);
		return false;
	}
	
	// open the file/pipe on stdin
	if(ov_open_callbacks(f,&oggFile,NULL,-1,OV_CALLBACKS_DEFAULT)==-1){
		std::stringstream str;
		str << "Could not open input as an OggVorbis file." << std::endl;
		coreGetLog()->WriteError(str.str(),__FILE__,__LINE__);
		return false;
	}
	
    // Get some information about the OGG file ( format, frequency...)
    m_pInfo = ov_info(&oggFile, -1);

	// Set filestate to open
	m_bFileOpen = true;	

	return true;
}

bool StreamingBuffer_OpenAL::close() {
	
	if (m_bFileOpen) {
		
		// Set filestate to closed
		m_bFileOpen = false;

		m_pInfo = NULL;

		// Clean up the oggFile
		ov_clear(&oggFile); 
	}

	return true;
}
}